package cae.logic.destination;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import cae.logic.util.CaeClipConfiguration;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;


public class CaeDestinatiosManager 
{
	private static CaeDestinatiosManager instance;
	private List<CaeDestination> destinations;
	private static final String FILE_NAME = "destinations.xml";
	private static XStream xstream = new XStream(new DomDriver());
	private static Logger logger = Logger.getLogger(CaeDestinatiosManager.class);
	private File destinationsFile;
	
	public static CaeDestinatiosManager getInstance()
	{
		if(instance == null)
			instance = new CaeDestinatiosManager();
		return instance;
	}
	
	private CaeDestinatiosManager()
	{
		xstream.alias("destination", CaeDestination.class);
		destinationsFile = new File(FILE_NAME);
		loadOrCreate();
		new Thread(new CaeDestinationTester()).start();
	}

	private void loadOrCreate()
	{
		if(!destinationsFile.exists())
		{
			destinationsFile.getParentFile().mkdirs();
			destinations = new ArrayList<CaeDestination>();
			save();
		}
		else
		{
			load();
		}
	}

	@SuppressWarnings("unchecked")
	private void load()
	{
		try
		{
			InputStream file = new FileInputStream(destinationsFile);
			destinations = (List<CaeDestination>)xstream.fromXML(file);
			file.close();
		}
		catch(Exception e)
		{
			logger.fatal("Error en la carga del archivo de destinations ", e);
			throw new RuntimeException("Error en la carga del archivo de destinations ", e);
		}
	}
	
	private void save()
	{
		try
		{
			OutputStream file = new FileOutputStream(destinationsFile);
			xstream.toXML(destinations, file);
			file.close();
		}
		catch(Exception e)
		{
			logger.fatal("Error en la escritura del archivo de destinations ", e);
			throw new RuntimeException("Error en la escritura del archivo de destinations ", e);
		}
	}

	public List<CaeDestination> getDestinations() {
		return destinations;
	}
	
	public void addDestination(CaeDestination dest) throws CaeDestinationException
	{
		if( getDestination(dest.getMachine()[0]) != null)
			throw new CaeDestinationException("El destination ya existe");
		destinations.add(dest);
		save();
	}
	
	public CaeDestination getDestination(String machineName)
	{
		for(CaeDestination d : destinations)
		{
			String[] machineNames = d.getMachine();
			for (String destMachineName : machineNames) {
				if(destMachineName.equalsIgnoreCase(machineName))
				{
					return d;
				}
			}
		}
		return null;
	}

	public CaeDestination getDestinationByAlias(String alias) 
	{
		for(CaeDestination d : destinations)
		{
			if(d.getAlias().equalsIgnoreCase(alias))
			{
				return d;
			}
		}
		return null;
	}

	public void removeDestination(CaeDestination dest) {
		destinations.remove(dest);
		save();
	}
	
	
	
	private class CaeDestinationTester implements Runnable
	{
		public void run()
		{
			while(true)
			{
				try
				{
					for (CaeDestination destination : destinations)
					{
						new Thread(new CaeDestinationSingleTester(destination)).start();
					}
				}
				catch(Exception e)
				{
					logger.error("Error al intentar testear destinations ",e);
				}
				
				try {
					Thread.sleep(CaeClipConfiguration.getInstance().getIProp(CaeClipConfiguration.TEME_BATWEEN_DESTINATION_TEST));
				} catch (InterruptedException e) {}
			}
		}
		
		private class CaeDestinationSingleTester implements Runnable
		{
			private CaeDestination destination;
			
			private CaeDestinationSingleTester(CaeDestination destination)
			{
				this.destination = destination;
			}
			
			public void run() {
				try {
					Socket s = new Socket();
					s.connect(new InetSocketAddress(destination.getMachine()[0], destination.getPort()));
					s.close();
				} catch (IOException e) {
					destination.setConnected(false);
					return;
				}
				destination.setConnected(true);
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public void setDestinations(URL url) throws IOException
	{
		URLConnection uc = url.openConnection();
		List<CaeDestination> dest = (List<CaeDestination>) xstream.fromXML( uc.getInputStream() );
		this.destinations = dest;
		save();
	}
}
